﻿#region License

// Another free, open solution by folks who get it.
// Copyright © 2011 Business in Unison, Inc.
// 
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

#endregion

using System;

using Castle.DynamicProxy;

namespace Patterns.Interception
{
	/// <summary>
	/// Defines an interceptor with lambda-driven behavior.
	/// </summary>
	public class LambdaInterceptor : IInterceptor
	{
		/// <summary>
		/// Gets or sets the before action.
		/// </summary>
		/// <value>The before action.</value>
		public Action<IInvocation> BeforeAction { get; set; }

		/// <summary>
		/// Gets or sets the after action.
		/// </summary>
		/// <value>The after action.</value>
		public Action<IInvocation> AfterAction { get; set; }

		/// <summary>
		/// Gets or sets the finally action.
		/// </summary>
		/// <value>The finally action.</value>
		public Action<IInvocation> FinallyAction { get; set; }

		/// <summary>
		/// Gets or sets the run condition action.
		/// </summary>
		/// <value>The run condition action.</value>
		public Func<IInvocation, bool> RunConditionAction { get; set; }

		/// <summary>
		/// Gets or sets the on error action.
		/// </summary>
		/// <value>The on error action.</value>
		public Func<IInvocation, Exception, bool> OnErrorAction { get; set; }

		#region IInterceptor Members

		/// <summary>
		/// Intercepts the specified invocation.
		/// </summary>
		/// <param name="invocation">The invocation.</param>
		public virtual void Intercept(IInvocation invocation)
		{
			if (RunConditionAction != null && !RunConditionAction(invocation))
			{
				invocation.Proceed();
				return;
			}

			try
			{
				if (BeforeAction != null) BeforeAction(invocation);

				invocation.Proceed();

				if (AfterAction != null) AfterAction(invocation);
			}
			catch (Exception ex)
			{
				if(OnErrorAction == null || !OnErrorAction(invocation, ex)) throw;
			}
			finally
			{
				if (FinallyAction != null) FinallyAction(invocation);
			}
		}

		#endregion

		/// <summary>
		/// Applies the specified behavior.
		/// </summary>
		/// <param name="before">The before action.</param>
		/// <returns></returns>
		public virtual LambdaInterceptor Before(Action<IInvocation> before)
		{
			BeforeAction = before;
			return this;
		}

		/// <summary>
		/// Applies the specified behavior.
		/// </summary>
		/// <param name="after">The after action.</param>
		/// <returns></returns>
		public virtual LambdaInterceptor After(Action<IInvocation> after)
		{
			AfterAction = after;
			return this;
		}

		/// <summary>
		/// Applies the specified behavior.
		/// </summary>
		/// <param name="finally">The finally action.</param>
		/// <returns></returns>
		public virtual LambdaInterceptor Finally(Action<IInvocation> @finally)
		{
			FinallyAction = @finally;
			return this;
		}

		/// <summary>
		/// Applies the specified behavior.
		/// </summary>
		/// <param name="runCondition">The run condition.</param>
		/// <returns></returns>
		public virtual LambdaInterceptor RunCondition(Func<IInvocation, bool> runCondition)
		{
			RunConditionAction = runCondition;
			return this;
		}

		/// <summary>
		/// Applies the specified behavior.
		/// </summary>
		/// <param name="onError">The on-error action.</param>
		/// <returns></returns>
		public virtual LambdaInterceptor OnError(Func<IInvocation, Exception, bool> onError)
		{
			OnErrorAction = onError;
			return this;
		}
	}
}